<?php
/**
 * Test anonymization functions.
 *
 * @package WordPress\UnitTests
 *
 * @since 4.9.6
 */

/**
 * Class Tests_Functions_Anonymization.
 *
 * @since 4.9.6
 *
 * @group functions.php
 * @group privacy
 * @covers ::wp_privacy_anonymize_data
 */
class Tests_Functions_Anonymization extends WP_UnitTestCase {

	/**
	 * Test that wp_privacy_anonymize_ip() properly anonymizes all possible IP address formats.
	 *
	 * @dataProvider data_wp_privacy_anonymize_ip
	 *
	 * @ticket 41083
	 * @ticket 43545
	 *
	 * @covers ::wp_privacy_anonymize_ip
	 *
	 * @param string $raw_ip          Raw IP address.
	 * @param string $expected_result Expected result.
	 */
	public function test_wp_privacy_anonymize_ip( $raw_ip, $expected_result ) {
		$actual_result = wp_privacy_anonymize_data( 'ip', $raw_ip );

		/* Todo test ipv6_fallback mode if keeping it.*/

		$this->assertSame( $expected_result, $actual_result );
	}

	/**
	 * Provide test cases for `test_wp_privacy_anonymize_ip()`.
	 *
	 * @since 4.9.6 Moved from `Test_WP_Community_Events::data_get_unsafe_client_ip_anonymization()`.
	 *
	 * @return array {
	 *     @type array {
	 *         @string string $raw_ip          Raw IP address.
	 *         @string string $expected_result Expected result.
	 *     }
	 * }
	 */
	public function data_wp_privacy_anonymize_ip() {
		return array(
			// Invalid IP.
			array(
				null,
				'0.0.0.0',
			),
			array(
				false,
				'0.0.0.0',
			),
			array(
				true,
				'0.0.0.0',
			),
			array(
				0,
				'0.0.0.0',
			),
			array(
				1,
				'0.0.0.0',
			),
			array(
				'',
				'0.0.0.0',
			),
			array(
				'0.0.0.0.0',
				'0.0.0.0',
			),
			array(
				'0000:0000:0000:0000:0000:0000:0127:2258',
				'::',
			),
			// Invalid IP. Sometimes proxies add things like this, or other arbitrary strings.
			array(
				'unknown',
				'0.0.0.0',
			),
			// Invalid IP. Sometimes proxies add things like this, or other arbitrary strings.
			array(
				'or=\"[1000:0000:0000:0000:0000:0000:0000:0001',
				'::',
			),
			// Invalid IP. Sometimes proxies add things like this, or other arbitrary strings.
			array(
				'or=\"1000:0000:0000:0000:0000:0000:0000:0001',
				'::',
			),
			// Invalid IP. Sometimes proxies add things like this, or other arbitrary strings.
			array(
				'1000:0000:0000:0000:0000:0000:0000:0001or=\"',
				'::',
			),
			// IPv4, no port.
			array(
				'10.20.30.45',
				'10.20.30.0',
			),
			// IPv4, port.
			array(
				'10.20.30.45:20000',
				'10.20.30.0',
			),
			// IPv4, netmask.
			array(
				'10.20.30.45/24',
				'10.20.30.0',
			),
			// IPv6, no port, reducible representation.
			array(
				'0000:0000:0000:0000:0000:0000:0000:0001',
				'::',
			),
			// IPv6, port, reducible representation.
			array(
				'[0000:0000:0000:0000:0000:0000:0000:0001]:1234',
				'::',
			),
			// IPv6, no port, reduced representation.
			array(
				'::',
				'::',
			),
			// IPv6, no port, reduced representation.
			array(
				'::1',
				'::',
			),
			// IPv6, port, reduced representation.
			array(
				'[::]:20000',
				'::',
			),
			// IPv6, address brackets without port delimiter and number, reduced representation.
			array(
				'[::1]',
				'::',
			),
			// IPv6, no port, compatibility mode.
			array(
				'::ffff:10.15.20.25',
				'::ffff:10.15.20.0',
			),
			// IPv6, port, compatibility mode.
			array(
				'[::FFFF:10.15.20.25]:30000',
				'::ffff:10.15.20.0',
			),
			// IPv6, no port, compatibility mode shorthand.
			array(
				'::127.0.0.1',
				'::ffff:127.0.0.0',
			),
			// IPv6, port, compatibility mode shorthand.
			array(
				'[::127.0.0.1]:30000',
				'::ffff:127.0.0.0',
			),
		);
	}

	/**
	 * Test that wp_privacy_anonymize_ip() properly anonymizes all possible IP address formats.
	 *
	 * @dataProvider data_wp_privacy_anonymize_ip_with_inet_dependency
	 *
	 * @ticket 41083
	 * @ticket 43545
	 * @requires function inet_ntop
	 * @requires function inet_pton
	 *
	 * @covers ::wp_privacy_anonymize_ip
	 *
	 * @param string $raw_ip          Raw IP address.
	 * @param string $expected_result Expected result.
	 */
	public function test_wp_privacy_anonymize_ip_with_inet_dependency( $raw_ip, $expected_result ) {
		$this->test_wp_privacy_anonymize_ip( $raw_ip, $expected_result );
	}

	/**
	 * Provide test cases for `test_wp_privacy_anonymize_ip()`.
	 *
	 * @since 4.9.6 Moved from `Test_WP_Community_Events::data_get_unsafe_client_ip_anonymization()`.
	 *
	 * @return array {
	 *     @type array {
	 *         @string string $raw_ip          Raw IP address.
	 *         @string string $expected_result Expected result.
	 *     }
	 * }
	 */
	public function data_wp_privacy_anonymize_ip_with_inet_dependency() {
		return array(
			// Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings.
			array(
				'or=\"[1000:0000:0000:0000:0000:0000:0000:0001]:400',
				'1000::',
			),
			// Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings.
			array(
				'or=\"[1000:0000:0000:0000:0000:0000:0000:0001]',
				'1000::',
			),
			// Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings.
			array(
				'or=\"[1000:0000:0000:0000:0000:0000:0000:0001]400',
				'1000::',
			),
			// Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings.
			array(
				'[1000:0000:0000:0000:0000:0000:0000:0001]:235\"or=',
				'1000::',
			),
			// IPv6, no port.
			array(
				'2a03:2880:2110:df07:face:b00c::1',
				'2a03:2880:2110:df07::',
			),
			// IPv6, port.
			array(
				'[2a03:2880:2110:df07:face:b00c::1]:20000',
				'2a03:2880:2110:df07::',
			),
			// IPv6, no port, partially reducible representation.
			array(
				'1000:0000:0000:0000:0000:0000:0000:0001',
				'1000::',
			),
			// IPv6, port, partially reducible representation.
			array(
				'[1000:0000:0000:0000:0000:0000:0000:0001]:5678',
				'1000::',
			),
			// IPv6 with reachability scope.
			array(
				'fe80::b059:65f4:e877:c40%16',
				'fe80::',
			),
			// IPv6 with reachability scope.
			array(
				'FE80::B059:65F4:E877:C40%eth0',
				'fe80::',
			),
		);
	}

	/**
	 * Test email anonymization of `wp_privacy_anonymize_data()`.
	 */
	public function test_anonymize_email() {
		$this->assertSame( 'deleted@site.invalid', wp_privacy_anonymize_data( 'email', 'bar@example.com' ) );
	}

	/**
	 * Test url anonymization of `wp_privacy_anonymize_data()`.
	 */
	public function test_anonymize_url() {
		$this->assertSame( 'https://site.invalid', wp_privacy_anonymize_data( 'url', 'https://example.com/author/username' ) );
	}

	/**
	 * Test date anonymization of `wp_privacy_anonymize_data()`.
	 */
	public function test_anonymize_date() {
		$this->assertSame( '0000-00-00 00:00:00', wp_privacy_anonymize_data( 'date', '2003-12-25 12:34:56' ) );
	}

	/**
	 * Test text anonymization of `wp_privacy_anonymize_data()`.
	 */
	public function test_anonymize_text() {
		$text = __( 'Four score and seven years ago' );
		$this->assertSame( '[deleted]', wp_privacy_anonymize_data( 'text', $text ) );
	}

	/**
	 * Test long text anonymization of `wp_privacy_anonymize_data()`.
	 */
	public function test_anonymize_long_text() {
		$text = __( 'Four score and seven years ago' );
		$this->assertSame( 'This content was deleted by the author.', wp_privacy_anonymize_data( 'longtext', $text ) );
	}

	/**
	 * Test text anonymization when a filter is added.
	 *
	 * @ticket 44141
	 */
	public function test_anonymize_with_filter() {
		add_filter( 'wp_privacy_anonymize_data', array( $this, 'filter_wp_privacy_anonymize_data' ), 10, 3 );
		$actual_url = wp_privacy_anonymize_data( 'url', 'https://example.com/author/username' );
		remove_filter( 'wp_privacy_anonymize_data', array( $this, 'filter_wp_privacy_anonymize_data' ), 10 );

		$this->assertSame( 'http://local.host/why-this-was-removed', $actual_url );
	}

	/**
	 * Change the anonymized value for URLs.
	 *
	 * @since 4.9.8
	 *
	 * @param string  $anonymous Anonymized data.
	 * @param string  $type      Type of the data.
	 * @param string  $data      Original data.
	 * @return string Anonymized data.
	 */
	public function filter_wp_privacy_anonymize_data( $anonymous, $type, $data ) {
		if ( 'url' === $type && 'example.com' === parse_url( $data, PHP_URL_HOST ) ) {
			return 'http://local.host/why-this-was-removed';
		}
		return $anonymous;
	}

}
